// Copyright  2004-2008 Backplane Ltd.
//
// This file is part of Ubiquity formsPlayer
//
// Ubiquity formsPlayer is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// Ubiquity formsPlayer is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License
// along with Ubiquity formsPlayer If not, see <http://www.gnu.org/licenses/>.
//	
//
//   Purpose: implementation of CXFormsElementGroup.
//   $Id$


#include "stdafx.h"
#include "XForms.h"
#include "XFormsApp.h"
#include "groupui.h"
#include "ImplGroup.h"
#include "parseGroup.h"
#include <HTMLDOMTranslator.h>
//////////////////////////////////////////////////////////////////////
// CXFormsElementGroup Class
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////


CXFormsElementGroup::CXFormsElementGroup()
:m_pOutOfLineElements(0)
{
//   m_sControlName = "group";
 //  m_bNewMethod = true;
}

CXFormsElementGroup::~CXFormsElementGroup()
{
	if(m_pOutOfLineElements && !m_pOutOfLineElements->empty())
	{
		CListOfIHTMLElements::iterator i = m_pOutOfLineElements->begin();
		while(i != m_pOutOfLineElements->end())
		{
			CComQIPtr<IHTMLElement2> pEl2 = *i;
			CComBSTR bs(L"onfocus");
			pEl2->detachEvent(bs,m_pEventSink);
			bs = L"onfocusin";
			pEl2->detachEvent(bs,m_pEventSink);
			bs = L"onfocusout";
			pEl2->detachEvent(bs,m_pEventSink);
	//		bs = L"onkeyup";
	//		pEl2->detachEvent(bs,m_pEventSink);
			++i;
		}
		m_pOutOfLineElements->clear();
	}
	delete m_pOutOfLineElements;
}

CImpl* CXFormsElementGroup::CreateImpl()
{
	CImpl* pFormControl = (CImpl*) new CImplGroup(this);

	return pFormControl;
}

CXFormsParse* CXFormsElementGroup::CreateParse(CImpl* pImpl)
{
	CXFormsParse* pParse = new CXFormsParseGroup((CImplGroup*) pImpl);

	return pParse;
}

HRESULT CXFormsElementGroup::InitBehaviour()
{
	HRESULT hr =  __super::InitBehaviour();
	
	CComPtr<IHTMLElement> pMaster;
	GetMasterElement(&pMaster);
	if(pMaster)
	{
		pMaster->setAttribute(CComBSTR(L"liveChildren"), CComVariant(L"yes"), 1);
		pMaster->setAttribute(CComBSTR(L"tabIndex"), CComVariant(-1, VT_I4), 1);
	}
	
	return hr;
}

HRESULT CXFormsElementGroup::DocumentReady(void)
{
	return 	__super::DocumentReady();
}

HRESULT CXFormsElementGroup::render()
{
	HRESULT hr = __super::render();
		//TODO: this is event attachment, not rendering.	
		CComObject<CEventSink> * pSink = 0;
		 hr = CComObject<CEventSink>::CreateInstance(&pSink);

		if(m_pFormControl) {
			pSink->m_pFormControl = m_pFormControl;
			pSink->m_pControl = this;
			pSink->m_bHandleThroughHandleEvent = true;

			m_pEventSink  = pSink;
			attachEvents();
		} else {
			hr = E_FAIL;
		}


	return hr;
}
const CComBSTR g_bsNodeProperty(L"Node");
HRESULT CXFormsElementGroup::attachEvents(void)
{	
	CComPtr<IHTMLElement> pMaster;
	HRESULT hr = GetMasterElement(&pMaster);
	if(SUCCEEDED(hr) && m_pEventSink)		
	{
		VARIANT_BOOL b;
		CComQIPtr<IHTMLElement2> pMaster2 = pMaster;
		CComBSTR bs(L"onfocus");
		hr = pMaster2->attachEvent(bs,m_pEventSink,&b);
		bs = L"onfocusin";
		hr = pMaster2->attachEvent(bs,m_pEventSink,&b);
		bs = L"onfocusout";
		hr = pMaster2->attachEvent(bs,m_pEventSink,&b);
	//	bs = L"onkeyup";
	//	hr = pMaster2->attachEvent(bs,m_pEventSink,&b);
	}
	CComQIPtr<IEventListener> pThisAsListener(this);
	//Check that this will be included in the DOM2Events tree
	//	Where a node has been added using addBehavior, in the case of
	//	groups being added to TR elements for grid repeats,
	//	The automatic aggregation that happens through normal
	//	behaviour insertion does not occur.
	CComQIPtr<INode> pMasterAsNode(pMaster);
	if(!pMasterAsNode)
	{
		//Oh No! The master element does not cast to an INode
		//	Add the Node property to it, so that DOM2Core can
		//	find it, thus enabling the proper bubbliciousness of events.
		CComQIPtr<INode> pNode(pThisAsListener);
		pMaster->setAttribute(g_bsNodeProperty,CComVariant(pNode),1);

	}

	//In the above situation, whereby the interfaces of the behaviour have not been aggregated 
	//	by the HTML element, the master will also not be an event target.  It may be a picosecond
	//	more efficient to move this into the above branch, and possibly abolish this teest,
	//	but there may be some situation that I cannot imagine right now, in which  Master is not a Node
	//	but it is an Event Target.  Possibly by the use of DOM2Events by some other behaviour system
	//	so probably best to keep it outside, so that they are separate tests.
	CComQIPtr<IEventTarget> pMasterAsTarget(pMaster);
	if(!pMasterAsTarget)
	{
		pMasterAsTarget = pThisAsListener;
	}		

	if(pThisAsListener && pMasterAsTarget)
	{
		pMasterAsTarget->addEventListener(EVENT_DOMFOCUS_IN,pThisAsListener,FALSE);
	}

	return hr;
}



STDMETHODIMP CXFormsElementGroup::handleEvent(IDOMEvent * pEvt)
{
	HRESULT hr = S_FALSE;
	if(pEvt && m_pFormControl)
	{
		CComBSTR sEventName;
		pEvt->get_type(&sEventName);
		if(sEventName == EVENT_DOMFOCUS_IN)
		{
			hr = dynamic_cast<CImplGroup*>(m_pFormControl)->onDOMFocusIn_DefaultBehaviour();
		}
	}
	return hr;
}

HRESULT CXFormsElementGroup::detachEvents() 
{
	CComPtr<IHTMLElement> pMaster;
	HRESULT hr = GetMasterElement(&pMaster);
	if(SUCCEEDED(hr))
	{
		CComQIPtr<IHTMLElement2> pMaster2 = pMaster;
		CComBSTR bs(L"onfocus");
		hr = pMaster2->detachEvent(bs,m_pEventSink);
		bs = L"onfocusin";
		hr = pMaster2->detachEvent(bs,m_pEventSink);
		bs = L"onfocusout";
		hr = pMaster2->detachEvent(bs,m_pEventSink);
	//	bs = L"onkeyup";
	//	hr = pMaster2->detachEvent(bs,m_pEventSink);
	}
	__super::detachEvents();
	CComQIPtr<IEventListener> pThisAsListener(this);
	CComQIPtr<IEventTarget> pMasterAsTarget(pMaster);
	if(pThisAsListener && pMasterAsTarget)
	{
		pMasterAsTarget->removeEventListener(EVENT_DOMFOCUS_IN,pThisAsListener,FALSE);
	}
	return hr;
}

HRESULT CXFormsElementGroup::put_XPathExpression(const BSTR bsXPath)
{
	m_sDebugInfo = bsXPath;

	return S_OK;
}

HRESULT CXFormsElementGroup::handleEvent(CComBSTR& sEventName, IHTMLEventObj2 * pEventObject,VARIANT * pvarResult)
{
	if(sEventName == L"focusin")
	{
		if(m_pFormControl)
			m_pFormControl->onDOMFocusIn();
	}
/*	else if(sEventName == L"keyup")
	{
		CImplGroup * pGroup = dynamic_cast<CImplGroup *>(m_pFormControl);
		if(pGroup && pGroup->m_bIsRepeatIteration)
		{
			CImplRepeat * pRepeat = dynamic_cast<CImplRepeat *>(pGroup->m_pParentImpl);
			CComQIPtr<IHTMLEventObj> pEventObject1 = pEventObject;
			if(pRepeat && pEventObject1)
			{
				long lCode;
				pEventObject1->get_keyCode(&lCode);
				if(lCode == 38)//KEY_UP
				{
					pRepeat->decrementIndex();
					pEventObject1->put_cancelBubble(VARIANT_TRUE);
					pEventObject1->put_returnValue(CComVariant(false));
					CComPtr<IHTMLElement> pEl;
					GetMasterElement(&pEl);
				}
				else if(lCode == 40)//KEY_DOWN
				{
					pRepeat->incrementIndex();
					pEventObject1->put_cancelBubble(VARIANT_TRUE);
					pEventObject1->put_returnValue(CComVariant(false));
					CComPtr<IHTMLElement> pEl;
					GetMasterElement(&pEl);
				}
			}
			
		}
		
	}*/
	return CXFormsElement::handleEvent( sEventName,  pEventObject, pvarResult);
}

HRESULT ReplaceClass(IHTMLElement * pEl, BSTR bsRemove, BSTR bsSetting)
{
	HRESULT hr = S_FALSE;
	CComBSTR bsClasses;
	pEl->get_className(&bsClasses);
	
	//replace the unsetting class with the setting class
	CStringW sClasses(bsClasses);

	bool bReplaceSucceeded = (bsRemove && sClasses.Replace(bsRemove,bsSetting));
	bool bAppended = false;
	if(
		//unsetting class specified, but absent in classname string
		(bsRemove && !bReplaceSucceeded)
		||
		//unsetting class not specified, and setting class absent
		(!bsRemove && sClasses.Find(bsSetting) == -1))
	{
		sClasses += " ";
		sClasses += bsSetting;
		bAppended = true;
	}

	if(bReplaceSucceeded || bAppended)
	{
		BSTR s = sClasses.AllocSysString();
		hr = pEl->put_className(s);
		::SysFreeString(s);
	}
	return hr;
}

HRESULT CXFormsElementGroup::highlight(bool bApply, bool bFocus)
{
	HRESULT hr = S_FALSE;
	CComPtr<IHTMLElement> pEl;
	GetMasterElement(&pEl);
	if(pEl)
	{
		
		if(bApply)
		{
			ReplaceClass(pEl,0,cssClassRepeatIndex);
	//		hr = pEl->put_className(cssClassRepeatIndex);
			if(m_pOutOfLineElements && !m_pOutOfLineElements->empty())
			{
				CListOfIHTMLElements::iterator i = m_pOutOfLineElements->begin();
				while(i != m_pOutOfLineElements->end())
				{
					CComQIPtr<IHTMLElement> pEl = *i;
					ReplaceClass(pEl,0,cssClassRepeatIndex);
					++i;
				}
			}
			if(false && bFocus)
			{
				CComQIPtr<IHTMLDOMNode> pNode(pEl);
				if(pNode)
				{
					CComPtr<IHTMLDOMNode> pFirstChild;
					pNode->get_firstChild(&pFirstChild);
					CComQIPtr<IHTMLElement2> pFirstChildEl(pFirstChild);
					if(pFirstChildEl)
					{
						pFirstChildEl->focus();
					}
				}
			}
		}
		else
		{
			ReplaceClass(pEl,cssClassRepeatIndex,0);
	//		hr = pEl->put_className(cssClassRepeatItem);	
			if(m_pOutOfLineElements && !m_pOutOfLineElements->empty())
			{
				CListOfIHTMLElements::iterator i = m_pOutOfLineElements->begin();
				while(i != m_pOutOfLineElements->end())
				{
					CComQIPtr<IHTMLElement> pEl = *i;
					ReplaceClass(pEl,cssClassRepeatIndex,0);
					++i;
				}
			}
		}
	}
	
	
	
	return hr;
}


HRESULT CXFormsElementGroup::AddOutOfLineElement(IHTMLElement * pEl)
{
	HRESULT hr = S_OK;
	if(!m_pOutOfLineElements)
		m_pOutOfLineElements = new CListOfIHTMLElements();
	if(m_pOutOfLineElements)
	{
		m_pOutOfLineElements->push_back(pEl);
		CComQIPtr<IHTMLElement2> pEl2 = pEl;
		if(m_pEventSink)
		{
			VARIANT_BOOL b;
			CComBSTR bs(L"onfocus");
			hr = pEl2->attachEvent(bs,m_pEventSink,&b);
			bs = L"onfocusin";
			hr = pEl2->attachEvent(bs,m_pEventSink,&b);
			bs = L"onfocusout";
			hr = pEl2->attachEvent(bs,m_pEventSink,&b);
		}
		CComQIPtr<IEventListener> pThisAsListener(this);
		CComQIPtr<IEventTarget> pOOLElementAsTarget(pEl);
		if(pThisAsListener && pOOLElementAsTarget)
		{
			pOOLElementAsTarget->addEventListener(EVENT_DOMFOCUS_IN,pThisAsListener,FALSE);
		}
	}
	return hr;
}

void CXFormsElementGroup::RegisterFindBehaviourRefusal()
{
	//do nothing
#ifdef _DEBUG
	long l = 0;
#endif
}
void CXFormsElementGroup::RegisterFindBehaviourAcceptance()
{
	//do nothing
#ifdef _DEBUG
	long l = 0;
#endif
}




HRESULT CXFormsElementGroup::getXML(IXMLDOMNode ** ppXML)
{
	HRESULT hr = E_NOINTERFACE;
	CComQIPtr<IHTMLDOMNode> pMaster = GetControllingUnknown();
	if(pMaster)
	{
		CHTMLDOMTranslator translator(CLSID_DOMDocument60);
		//no need to go deep, and doing so will cause bad performance issues.
		hr = translator.cloneNode(pMaster,ppXML,false);
	}
	return hr;

}
